//This file contains the logging middleware.
import 'dart:async';

import 'package:simple_server/src/context.dart';
import 'package:simple_server/src/header.dart';
import 'package:simple_server/src/json.dart';
import 'package:simple_server/src/log.dart';
import 'package:simple_server/src/middleware/middleware.dart';

typedef ClientIpListener = FutureOr<void> Function(Context context, String clientIP);

class LoggingMiddleware<T extends Context> extends Middleware<T> {
  final _clientIPListeners = <ClientIpListener>{};

  void addClientIpListener(ClientIpListener listener) => _clientIPListeners.add(listener);

  void removeClientIpListener(ClientIpListener listener) => _clientIPListeners.remove(listener);

  @override
  Future<void> call(T context, Next next) async {
    final start = DateTime.now();
    final headers = context.request.headers.toFullMap().toSimpleMap();
    final logHeaders = headers.isNotEmpty;
    final prefix = [
      '$start [${context.method}] ${context.path}',
      'requestedUri: ${context.request.requestedUri}',
      if (logHeaders) 'headers: ${jsonPretty(headers)}',
    ];
    log('start - ${prefix[0]}');
    log(prefix[1], false);
    if (logHeaders) log(prefix[2], false);
    try {
      await next();
    } finally {
      log('end   - ${prefix[0]}');
      log(prefix[1], false);
      if (logHeaders) log(prefix[2], false);
      final end = DateTime.now();
      final time = end.difference(start).inMilliseconds;
      final clientIP = context['cf-connecting-ip'] ?? context['x-forwarded-for'];
      if (clientIP != null) log('clientIp: $clientIP');
      if (context.hasData) log('context: ${context.toLimitString(1000)}');
      log('code: ${context.response.statusCode}, cost: ${time}ms\n');
      if (context.auth) await _bindIP(context, clientIP);
    }
  }

  Future<void> _bindIP(T context, String? clientIP) async {
    if (clientIP == null) return;
    for (final listener in _clientIPListeners) {
      await listener(context, clientIP);
    }
  }
}
